"""Simple extendable yaml dicts."""
import copy
import typing

from . import misc


def merge_dicts(config: typing.Dict[str, typing.Any],
                new: typing.Mapping[str, typing.Any],
                ) -> typing.Dict[str, typing.Any]:
    """Recursively merge values from new into config dictionary."""
    for key, value in new.items():
        if (key in config and
                isinstance(value, dict) and isinstance(config[key], dict)):
            merge_dicts(config[key], value)
        else:
            config[key] = copy.deepcopy(value)
    return config


def _merged(configs: typing.Dict[str, typing.Any],
            key: str,
            use_default: bool = False,
            ) -> typing.Dict[str, typing.Any]:
    if use_default and '.default' in configs:
        config = _merged(configs, '.default')
    else:
        config = {}
    merge_config = configs[key] or {}
    for extend in misc.flattened(merge_config.get('.extends', [])):
        merge_dicts(config, _merged(configs, extend))
    merge_dicts(config, merge_config)
    config.pop('.extends', None)
    return config


def process_config_tree(configs: typing.Dict[str, typing.Any],
                        ) -> typing.Dict[str, typing.Any]:
    """Remove configurations starting with a dot, and process .extends.

    This is similar to the `extends` functionality of GitLab .gitlab-ci.yml
    files. For a dict of configuration dicts, a member `.extends` in a
    configuration dict can specify the name or list of names of other
    configuration dicts to inherit from.

    A `.default` configuration will be used for inheritance even if .extends is
    not specified.

    All configurations with a name starting with a dot will be removed.
    """
    return {k: _merged(configs, k, True) for k in configs.keys()
            if not k.startswith('.')}


def clean_config(value: typing.Any) -> typing.Any:
    """Remove dict members starting with a dot."""
    if isinstance(value, dict):
        return {k: clean_config(v) for k, v in value.items()
                if not k.startswith('.')}
    if isinstance(value, list):
        return [clean_config(v) for v in value]
    return value
